/*
 * Copyright (c) 2025 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the 'License');
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import { ArkFile, ts, AstTreeUtils } from "arkanalyzer/lib";
import { BaseChecker, BaseMetaData } from "../BaseChecker";
import Logger, { LOG_MODULE_TYPE } from "arkanalyzer/lib/utils/logger";
import { RuleListUtil } from "../../utils/common/DefectsList";
import { Defects, IssueReport } from "../../model/Defects";
import { Rule } from "../../model/Rule";
import { FileMatcher, MatcherCallback, MatcherTypes } from "../../Index";

const logger = Logger.getLogger(
  LOG_MODULE_TYPE.HOMECHECK,
  "NamingConventionCheck"
);

type Selector =
  | 'default'
  | 'variable'
  | 'function'
  | 'parameter'
  | 'property'
  | 'parameterProperty'
  | 'method'
  | 'accessor'
  | 'enumMember'
  | 'class'
  | 'interface'
  | 'typeAlias'
  | 'enum'
  | 'typeParameter'
  | 'memberLike'
  | 'typeLike'
  | 'variableLike'
  | 'classMethod'
  | 'objectLiteralMethod'
  | 'typeMethod'
  | 'classProperty'
  | 'objectLiteralProperty'
  | 'typeProperty'
  | 'import';

type Format =
  | 'camelCase'
  | 'strictCamelCase'
  | 'PascalCase'
  | 'StrictPascalCase'
  | 'snake_case'
  | 'UPPER_CASE';

type Modifier =
  | 'abstract'
  | 'async'
  | 'const'
  | 'default'
  | 'destructured'
  | 'exported'
  | 'global'
  | 'namespace'
  | 'override'
  | '#private'
  | 'private'
  | 'protected'
  | 'public'
  | 'readonly'
  | 'requiresQuotes'
  | 'static'
  | 'unused';

type TypeOption = 'array' | 'boolean' | 'function' | 'number' | 'string';

type Option = {
  selector: Selector | Selector[];
  format?: Format[] | null;
  modifiers?: Modifier[];
  types?: TypeOption[];
  filter?:
    | string
    | {
        regex: string;
        match: boolean;
      };
  custom?: {
    regex: string;
    match: boolean;
  };
  leadingUnderscore?:
    | 'forbid'
    | 'require'
    | 'requireDouble'
    | 'allow'
    | 'allowDouble'
    | 'allowSingleOrDouble';
  trailingUnderscore?:
    | 'forbid'
    | 'require'
    | 'requireDouble'
    | 'allow'
    | 'allowDouble'
    | 'allowSingleOrDouble';
  prefix?: string[];
  suffix?: string[];
};

const defaultOptions: Option[] = [
  {
    selector: 'default',
    format: ['camelCase'],
    leadingUnderscore: 'allow',
    trailingUnderscore: 'allow',
  },
  {
    selector: 'import',
    format: ['camelCase', 'PascalCase'],
  },
  {
    selector: 'variable',
    format: ['camelCase', 'UPPER_CASE'],
    leadingUnderscore: 'allow',
    trailingUnderscore: 'allow',
  },
  {
    selector: 'typeLike',
    format: ['PascalCase'],
  },
];

export class NamingConventionCheck implements BaseChecker {
  readonly metaData: BaseMetaData = {
    severity: 2,
    ruleDocPath: "docs/naming-convention-check.md",
    description: "Enforce naming conventions for everything across a codebase.",
  };

  public issues: any[] = [];
  public defects: Defects[] = [];
  public rule: Rule;
  private currentArkFile: ArkFile | null = null;
  private fileMatcher: FileMatcher = {
    matcherType: MatcherTypes.FILE,
  };
  private selectors: string[] = [];
  private formats: Format[] = [];
  private ruleConfigs = {
    prefixes: new Set<string>(),
    suffixes: new Set<string>(),
  };

  private options: Option[];
  private methodArr: ts.Node[] = [];
  private defaultArr: ts.Node[] = [];
  private variableArr: ts.Node[] = [];
  private functionArr: ts.Node[] = [];
  private parameterArr: ts.Node[] = [];
  private parameterPropertyArr: ts.Node[] = [];
  private accessorArr: ts.Node[] = [];
  private enumMemberArr: ts.Node[] = [];
  private classArr: ts.Node[] = [];
  private interfaceArr: ts.Node[] = [];
  private typeAliasArr: ts.Node[] = [];
  private enumArr: ts.Node[] = [];
  private typeParameterArr: ts.Node[] = [];
  private memberLikeArr: ts.Node[] = [];
  private typeLikeArr: ts.Node[] = [];
  private variableLikeArr: ts.Node[] = [];
  private classMethodArr: ts.Node[] = [];
  private objectLiteralMethodArr: ts.Node[] = [];
  private typeMethodArr: ts.Node[] = [];
  private classPropertyArr: ts.Node[] = [];
  private objectLiteralPropertyArr: ts.Node[] = [];
  private typePropertyArr: ts.PropertySignature[] = [];
  private importArr: ts.ImportDeclaration[] = [];
  private propertyArr: ts.Node[] = [];

  constructor(customOption?: Option[]) {
    if (customOption) {
      this.options = customOption;
    }
  }

  public registerMatchers(): MatcherCallback[] {
    const matchFileCb: MatcherCallback = {
      matcher: this.fileMatcher,
      callback: this.check,
    };
    return [matchFileCb];
  }

  public check = (targetField: ArkFile): void => {
    this.currentArkFile = targetField;
    const filePath = targetField.getFilePath();
    const code = targetField.getCode();
    const node = AstTreeUtils.getASTNode(filePath, code);

    this.options = this.rule.option?.length
      ? (this.rule.option as Option[])
      : defaultOptions;

    this.getNodeArr(node);

    const defaultItems = this.options.filter(
      (item) => item.selector === 'default'
    );

    const nonDefaultItems = this.options.filter(
      (item) => item.selector !== 'default'
    );

    const result1: Option[] = [...nonDefaultItems, ...defaultItems];

    const defaultItems1 = result1.filter((item) => item.modifiers);

    const nonDefaultItems1 = result1.filter((item) => !item.modifiers);

    const result: Option[] = [...nonDefaultItems1, ...defaultItems1];

    result.forEach((option) => {
      if (Array.isArray(option.selector)) {
        option.selector.forEach((selector) => {
          this.getOptionForSelector(selector, option);
          this.selectors.push(selector);
        });
      } else {
        this.getOptionForSelector(option.selector, option);
        this.selectors.push(option.selector);
      }

      option.format?.forEach((format) => {
        this.formats.push(format);
      });

      option.prefix?.forEach((p) => this.ruleConfigs.prefixes.add(p));

      option.suffix?.forEach((s) => this.ruleConfigs.suffixes.add(s));
    });
  };

  private getNodeArr(node: ts.Node) {
    const visit = (node: ts.Node) => {
      if (ts.isVariableDeclaration(node)) {
        this.variableArr.push(node);
      }
      if (ts.isFunctionDeclaration(node) || ts.isFunctionExpression(node)) {
        this.functionArr.push(node);
      }
      if (ts.isClassDeclaration(node)) {
        this.classArr.push(node);
      }
      if (ts.isInterfaceDeclaration(node)) {
        this.interfaceArr.push(node);
      }
      if (ts.isEnumDeclaration(node)) {
        this.enumArr.push(node);
      }
      if (ts.isMethodDeclaration(node)) {
        this.methodArr.push(node);
      }
      if (ts.isPropertyDeclaration(node)) {
        this.propertyArr.push(node);
      }

      if (ts.isParameter(node)) {
        this.parameterArr.push(node);
      }
      if (ts.isImportDeclaration(node)) {
        this.importArr.push(node);
      }
      if (ts.isParameterPropertyDeclaration(node, node.parent)) {
        this.parameterPropertyArr.push(node);
      }

      if (ts.isGetAccessor(node) || ts.isSetAccessor(node)) {
        this.accessorArr.push(node);
      }
      if (ts.isEnumMember(node)) {
        this.enumMemberArr.push(node);
      }
      if (ts.isTypeAliasDeclaration(node)) {
        this.typeAliasArr.push(node);
      }
      if (ts.isTypeParameterDeclaration(node)) {
        this.typeParameterArr.push(node);
      }
      if (
        ts.isMethodDeclaration(node) ||
        ts.isPropertyDeclaration(node) ||
        ts.isGetAccessor(node) ||
        ts.isSetAccessor(node) ||
        ts.isEnumMember(node)
      ) {
        this.memberLikeArr.push(node);
      }
      if (
        ts.isClassDeclaration(node) ||
        ts.isInterfaceDeclaration(node) ||
        ts.isTypeAliasDeclaration(node) ||
        ts.isEnumDeclaration(node) ||
        ts.isTypeParameterDeclaration(node)
      ) {
        this.typeLikeArr.push(node);
      }
      if (
        ts.isVariableDeclaration(node) ||
        ts.isFunctionDeclaration(node) ||
        ts.isParameter(node) ||
        ts.isParameterPropertyDeclaration(node, node.parent)
      ) {
        this.variableLikeArr.push(node);
      }
      if (ts.isMethodDeclaration(node) && ts.isClassDeclaration(node.parent)) {
        this.classMethodArr.push(node);
      }
      if (
        ts.isMethodDeclaration(node) &&
        ts.isObjectLiteralExpression(node.parent)
      ) {
        this.objectLiteralMethodArr.push(node);
      }
      if (ts.isMethodSignature(node)) {
        this.typeMethodArr.push(node);
      }
      if (
        ts.isPropertyDeclaration(node) &&
        ts.isClassDeclaration(node.parent)
      ) {
        this.classPropertyArr.push(node);
      }
      if (
        ts.isPropertyAssignment(node) &&
        ts.isObjectLiteralExpression(node.parent)
      ) {
        this.objectLiteralPropertyArr.push(node);
      }
      if (ts.isPropertySignature(node)) {
        this.typePropertyArr.push(node);
      }
      if (ts.isIdentifier(node)) {
        this.defaultArr.push(node);
      }
      ts.forEachChild(node, visit);
    };
    visit(node);
  }
  private checkParamMap: Map<string, number> = new Map();
  private getOptionForSelector(selector: Selector, option: Option) {
    const exhaustiveCheck = (s: never) => {
      throw new Error(`未处理的选择器类型: ${s}`);
    };
    switch (selector) {
      case 'variable':
        this.variableArr.forEach((node) => {
          this.checkVariable(node as ts.VariableDeclaration, option);
        });
        break;

      case 'function':
        this.functionArr.forEach((node) => {
          this.checkFunction(
            node as ts.FunctionDeclaration | ts.FunctionExpression,
            option
          );
        });
        break;

      case 'class':
        this.classArr.forEach((node) => {
          this.checkClass(node as ts.ClassDeclaration, option);
        });
        break;

      case 'interface':
        this.interfaceArr.forEach((node) => {
          this.checkInterface(node, option);
        });
        break;

      case 'enum':
        this.enumArr.forEach((node) => {
          this.checkEnum(node, option);
        });
        break;

      case 'parameter':
        this.parameterArr.forEach((node) => {
          this.checkParameter(node, option);
        });
        break;

      case 'import':
        this.importArr.forEach((node) => {
          if (node.importClause) {
            const namedBindings = node.importClause.namedBindings;
            if (namedBindings && ts.isNamedImports(namedBindings)) {
              namedBindings.elements.forEach((element) => {
                this.checkImport(element, option);
              });
            }
          }
        });
        break;
      case 'parameterProperty':
        this.parameterPropertyArr.forEach((node) => {
          this.checkParameterProperty(node, option);
        });
        break;

      case 'accessor':
        this.accessorArr.forEach((node) => {
          this.checkAccessor(node, option);
        });
        break;

      case 'enumMember':
        this.enumMemberArr.forEach((node) => {
          this.checkEnumMember(node, option);
        });
        break;

      case 'typeAlias':
        this.typeAliasArr.forEach((node) => {
          this.checkTypeAlias(node, option);
        });
        break;

      case 'typeParameter':
        this.typeParameterArr.forEach((node) => {
          this.checkTypeParameter(node, option);
        });
        break;

      case 'classMethod':
        this.classMethodArr.forEach((node) => {
          this.checkClassMethod(node, option);
        });
        break;

      case 'objectLiteralMethod':
        this.objectLiteralMethodArr.forEach((node) => {
          this.checkObjectLiteralMethod(node, option);
        });
        break;

      case 'typeMethod':
        this.typeMethodArr.forEach((node) => {
          this.checkTypeMethod(node, option);
        });
        break;

      case 'classProperty':
        this.classPropertyArr.forEach((node) => {
          this.checkClassProperty(node, option);
        });
        break;

      case 'objectLiteralProperty':
        this.objectLiteralPropertyArr.forEach((node) => {
          this.checkObjectLiteralProperty(node, option);
        });
        break;

      case 'typeProperty':
        this.typePropertyArr.forEach((node) => {
          this.checkTypeProperty(node, option);
        });
        break;
      case 'memberLike':
        this.memberLikeArr.forEach((node) => {
          this.checkMemberLike(node, option);
        });
        break;
      case 'method':
        this.methodArr.forEach((node) => {
          this.checkMethod(node as ts.MethodDeclaration, option);
        });
        break;
      case 'property':
        this.propertyArr.forEach((node) => {
          this.checkProperty(node, option);
        });
        break;
      case 'typeLike':
        this.typeLikeArr.forEach((node) => {
          this.checkTypeLike(node, option);
        });
        break;
      case 'variableLike':
        this.variableLikeArr.forEach((node) => {
          this.checkVariableLike(node, option);
        });
        break;
      case 'default':
        this.defaultArr.forEach((node) => {
          this.checkDefault(node as ts.Identifier, option);
        });
        break;

      default:
        exhaustiveCheck(selector);
    }
  }

  private checkDefault(node: ts.Identifier, option: Option): void {
    let kind = this.getIdentifierKind(node);
    this.checkNaming(node, node.text, option, kind);
    const [line, character] = this.getNodePosition(node);
    this.checkParamMap.set(node.text, line);
  }

  private getIdentifierKind(node: ts.Identifier): string {
    const parent = node.parent;
    
    if (!parent) {
        return 'Default';
    }

    if (ts.isClassDeclaration(parent)) {
        return 'Class';
    }

    if (ts.isMethodDeclaration(parent)) {
        if (ts.isClassDeclaration(parent.parent)) {
            return 'Class Method';
        }
        return 'Method';
    }

    if (ts.isFunctionDeclaration(parent)) {
        return 'Function';
    }

    if (ts.isParameter(parent)) {
        if (ts.isMethodDeclaration(parent.parent)) {
            return 'Method Parameter';
        }
        if (ts.isFunctionDeclaration(parent.parent)) {
            return 'Function Parameter';
        }
        return 'Parameter';
    }

    if (ts.isPropertyDeclaration(parent)) {
        return 'Class Property';
    }

    if (ts.isVariableDeclaration(parent)) {
        return 'Variable';
    }

    if (ts.isInterfaceDeclaration(parent)) {
        return 'Interface';
    }

    if (ts.isTypeAliasDeclaration(parent)) {
        return 'Type Alias';
    }

    if (ts.isEnumDeclaration(parent)) {
        return 'Enum';
    }

    if (ts.isEnumMember(parent)) {
        return 'Enum Member';
    }

    if (ts.isImportSpecifier(parent) || ts.isImportClause(parent)) {
        return 'Import';
    }

    if (ts.isExportSpecifier(parent)) {
        return 'Export';
    }

    if (ts.isGetAccessor(parent)) {
        return 'Getter';
    }
    if (ts.isSetAccessor(parent)) {
        return 'Setter';
    }

    if (ts.isTypeParameterDeclaration(parent)) {
        return 'Type Parameter';
    }

    return 'Default';
  }

  private checkVariable(node: ts.VariableDeclaration, option: Option): void {
    if (ts.isIdentifier(node.name)) {
      this.checkNaming(node, node.name.text, option, 'Variable');
      if (!option.prefix && !option.suffix) {
        const [line, character] = this.getNodePosition(node);
        this.checkParamMap.set(node.name.text, line);
      }
    }
  }

  private checkFunction(
    node: ts.FunctionDeclaration | ts.FunctionExpression,
    option: Option
  ): void {
    if (node.name && ts.isIdentifier(node.name)) {
      this.checkNaming(node, node.name.text, option, 'Function');
      if (!option.prefix && !option.suffix) {
        const [line, character] = this.getNodePosition(node);
        this.checkParamMap.set(node.name.text, line);
      }
    }
  }

  private checkParameter(node: ts.Node, option: Option): void {
    this.checkNaming(node, node.getText(), option, 'Parameter');
    const [line, character] = this.getNodePosition(node);
    this.checkParamMap.set(node.getText(), line);
  }

  private checkProperty(node: ts.Node, option: Option): void {
    const kind = this.getPropertyKind(node);
    if (ts.isPropertyDeclaration(node)) {
      this.checkNaming(node, node.name.getText(), option, kind);
      this.propertyArr.push(node);
    }
    const name = ts.isIdentifier(node) ? node.text : node.getText();
    if (!option.prefix && !option.suffix) {
      const [line, character] = this.getNodePosition(node);
      this.checkParamMap.set(name, line);
    }
  }

  private getPropertyKind(node: ts.Node): string {
    if (ts.isPropertyDeclaration(node) && ts.isClassDeclaration(node.parent)) {
        return 'Class Property';
    }
    
    if (ts.isPropertyAssignment(node) && ts.isObjectLiteralExpression(node.parent)) {
        return 'Object Literal Property';
    }
    
    if (ts.isPropertySignature(node)) {
        return 'Type Property';
    }
    
    return 'Property';
  }

  private checkParameterProperty(node: ts.Node, option: Option): void {
    this.checkNaming(node, node.getText(), option, 'ParameterProperty');
    if (!option.prefix && !option.suffix) {
      const [line, character] = this.getNodePosition(node);
      this.checkParamMap.set(node.getText(), line);
    }
  }

  private checkMethod(node: ts.MethodDeclaration, option: Option): void {
    if (ts.isIdentifier(node.name)) {
      const kind = this.getMethodKind(node);
      this.checkNaming(node, node.name.text, option, kind);
      if (!option.prefix && !option.suffix) {
        const [line, character] = this.getNodePosition(node);
        this.checkParamMap.set(node.name.text, line);
      }
    }
  }

  private getMethodKind(node: ts.MethodDeclaration): string {
    if (ts.isClassDeclaration(node.parent)) {
        return 'Class Method';
    }
    
    if (ts.isObjectLiteralExpression(node.parent)) {
        return 'Object Literal Method';
    }
    
    if (ts.isInterfaceDeclaration(node.parent) || ts.isTypeLiteralNode(node.parent)) {
        return 'Type Method';
    }
    
    return 'Method';
  }

  private checkAccessor(node: ts.Node, option: Option): void {
    this.checkNaming(node, node.getText(), option, 'Accessor');
    if (!option.prefix && !option.suffix) {
      const [line, character] = this.getNodePosition(node);
      this.checkParamMap.set(node.getText(), line);
    }
  }

  private checkEnumMember(node: ts.Node, option: Option): void {
    this.checkNaming(node, node.getText(), option, 'EnumMember');
    if (!option.prefix && !option.suffix) {
      const [line, character] = this.getNodePosition(node);
      this.checkParamMap.set(node.getText(), line);
    }
  }

  private checkClass(node: ts.ClassDeclaration, option: Option): void {
    if (node.name) {
      this.checkNaming(node, node.name.text, option, 'Class');
      if (!option.prefix && !option.suffix) {
        const [line, character] = this.getNodePosition(node);
        this.checkParamMap.set(node.name.text, line);
      }
    }
  }

  private checkInterface(node: ts.Node, option: Option): void {
    this.checkNaming(node, node.getText(), option, 'Interface');
    if (!option.prefix && !option.suffix) {
      const [line, character] = this.getNodePosition(node);
      this.checkParamMap.set(node.getText(), line);
    }
  }

  private checkTypeAlias(node: ts.Node, option: Option): void {
    this.checkNaming(node, node.getText(), option, 'TypeAlias');
    if (!option.prefix && !option.suffix) {
      const [line, character] = this.getNodePosition(node);
      this.checkParamMap.set(node.getText(), line);
    }
  }

  private checkEnum(node: ts.Node, option: Option): void {
    this.checkNaming(node, node.getText(), option, 'Enum');
    if (!option.prefix && !option.suffix) {
      const [line, character] = this.getNodePosition(node);
      this.checkParamMap.set(node.getText(), line);
    }
  }

  private checkTypeParameter(node: ts.Node, option: Option): void {
    this.checkNaming(node, node.getText(), option, 'TypeParameter');
    if (!option.prefix && !option.suffix) {
      const [line, character] = this.getNodePosition(node);
      this.checkParamMap.set(node.getText(), line);
    }
  }

  private checkMemberLike(node: ts.Node, option: Option): void {
    let kind = this.getMemberLikeKind(node);
    let name = this.getMemberName(node);
    
    if (name) {
        this.checkNaming(node, name, option, kind);
        if (!option.prefix && !option.suffix) {
            const [line, character] = this.getNodePosition(node);
            this.checkParamMap.set(name, line);
        }
    }
  }

  private getMemberLikeKind(node: ts.Node): string {
    if (ts.isGetAccessor(node)) {
        return 'Accessor';
    }
    if (ts.isSetAccessor(node)) {
        return 'Accessor';
    }

    if (ts.isEnumMember(node)) {
        return 'Enum Member';
    }

    if (ts.isMethodDeclaration(node)) {
        if (ts.isClassDeclaration(node.parent)) {
            return 'Class Method';
        }
        if (ts.isObjectLiteralExpression(node.parent)) {
            return 'Object Literal Method';
        }
        return 'Method';
    }

    if (ts.isParameter(node) && node.parent && ts.isConstructorDeclaration(node.parent)) {
        return 'Parameter Property';
    }

    if (ts.isPropertyDeclaration(node)) {
        if (ts.isClassDeclaration(node.parent)) {
            return 'Class Property';
        }
        return 'Property';
    }
    if (ts.isPropertyAssignment(node)) {
        return 'Object Literal Property';
    }
    if (ts.isPropertySignature(node)) {
        return 'Type Property';
    }

    return 'MemberLike';
  }

  private getMemberName(node: ts.Node): string | undefined {
    if (ts.isIdentifier(node)) {
        return node.text;
    }
    
    if ('name' in node) {
        const name = (node as any).name;
        if (ts.isIdentifier(name)) {
            return name.text;
        }
        if (ts.isPrivateIdentifier(name)) {
            return name.text;
        }
    }
    
    return node.getText();
  }

  private checkTypeLike(node: ts.Node, option: Option): void {
    const kind = this.getTypeLikeKind(node);
    let name = this.getTypeLikeName(node);
    
    if (name) {
        this.checkNaming(node, name, option, kind);
        if (!option.prefix && !option.suffix) {
            const [line, character] = this.getNodePosition(node);
            this.checkParamMap.set(name, line);
        }
    }
  }

  private getTypeLikeKind(node: ts.Node): string {
    if (ts.isClassDeclaration(node)) {
        return 'Class';
    }
    
    if (ts.isEnumDeclaration(node)) {
        return 'Enum';
    }
    
    if (ts.isInterfaceDeclaration(node)) {
        return 'Interface';
    }
    
    if (ts.isTypeAliasDeclaration(node)) {
        return 'Type Alias';
    }
    
    if (ts.isTypeParameterDeclaration(node)) {
        return 'Type Parameter';
    }
    
    return 'TypeLike';
  }

  private getTypeLikeName(node: ts.Node): string | undefined {
    if (ts.isClassDeclaration(node) || 
        ts.isInterfaceDeclaration(node) || 
        ts.isTypeAliasDeclaration(node) || 
        ts.isEnumDeclaration(node)) {
        return node.name?.text;
    }
    
    if (ts.isTypeParameterDeclaration(node)) {
        return node.name.text;
    }
    
    return node.getText();
  }

  private checkVariableLike(node: ts.Node, option: Option): void {
    const kind = this.getVariableLikeKind(node);
    const name = ts.isIdentifier(node) ? node.text : node.getText();
    
    this.checkNaming(node, name, option, kind);
    if (!option.prefix && !option.suffix) {
        const [line, character] = this.getNodePosition(node);
        this.checkParamMap.set(name, line);
    }
  }

  private getVariableLikeKind(node: ts.Node): string {
    if (ts.isFunctionDeclaration(node)) {
        return 'Function';
    }
    
    if (ts.isParameter(node)) {
        if (ts.isMethodDeclaration(node.parent)) {
            return 'Method Parameter';
        }
        if (ts.isFunctionDeclaration(node.parent)) {
            return 'Function Parameter';
        }
        return 'Parameter';
    }
    
    if (ts.isVariableDeclaration(node)) {
        return 'Variable';
    }
    
    return 'VariableLike';
  }

  private checkClassMethod(node: ts.Node, option: Option): void {
    this.checkNaming(node, node.getText(), option, 'ClassMethod');
    if (!option.prefix && !option.suffix) {
      const [line, character] = this.getNodePosition(node);
      this.checkParamMap.set(node.getText(), line);
    }
  }

  private checkObjectLiteralMethod(node: ts.Node, option: Option): void {
    this.checkNaming(node, node.getText(), option, 'ObjectLiteralMethod');
    if (!option.prefix && !option.suffix) {
      const [line, character] = this.getNodePosition(node);
      this.checkParamMap.set(node.getText(), line);
    }
  }

  private checkTypeMethod(node: ts.Node, option: Option): void {
    this.checkNaming(node, node.getText(), option, 'TypeMethod');
    if (!option.prefix && !option.suffix) {
      const [line, character] = this.getNodePosition(node);
      this.checkParamMap.set(node.getText(), line);
    }
  }

  private checkClassProperty(node: ts.Node, option: Option): void {
    this.checkNaming(node, node.getText(), option, 'ClassProperty');
    if (!option.prefix && !option.suffix) {
      const [line, character] = this.getNodePosition(node);
      this.checkParamMap.set(node.getText(), line);
    }
  }

  private checkObjectLiteralProperty(node: ts.Node, option: Option): void {
    this.checkNaming(node, node.getText(), option, 'ObjectLiteralProperty');
    if (!option.prefix && !option.suffix) {
      const [line, character] = this.getNodePosition(node);
      this.checkParamMap.set(node.getText(), line);
    }
  }

  private checkTypeProperty(node: ts.PropertySignature, option: Option): void {
    this.checkNaming(node, node.name.getText(), option, 'TypeProperty');
    if (!option.prefix && !option.suffix) {
      const [line, character] = this.getNodePosition(node);
      this.checkParamMap.set(node.name.getText(), line);
    }
  }

  private checkImport(node: ts.Node, option: Option): void {
    if (ts.isImportSpecifier(node)) {
      const importName = node.name.text;
      this.checkNaming(node, importName, option, 'Import');
      if (!option.prefix && !option.suffix) {
        const [line, character] = this.getNodePosition(node);
        this.checkParamMap.set(importName, line);
      }
    }
  }

  private checkModifiers(node: ts.Node, option: Option): boolean {
    if (!option.modifiers || option.modifiers.length === 0) {
        return true;
    }

    const nodeModifiers = ts.canHaveModifiers(node)
        ? ts.getModifiers(node)
        : undefined;
    const modifierKinds = new Set(
        nodeModifiers?.map((modifier) => modifier.kind)
    );

    return option.modifiers.every((modifier) => 
        this.checkSingleModifier(node, modifier, modifierKinds));
  }

  private checkSingleModifier(
    node: ts.Node, 
    modifier: Modifier, 
    modifierKinds: Set<number>
  ): boolean {
    switch (modifier) {
        case 'abstract':
        case 'async':
        case 'exported':
        case 'private':
        case 'protected':
        case 'readonly':
        case 'static':
            return this.checkSimpleModifier(modifier, modifierKinds);
        case 'const':
            return this.checkConstModifier(node);
        case 'global':
            return this.checkGlobalModifier(node);
        case '#private':
            return this.checkPrivateIdentifier(node);
        case 'public':
            return this.checkPublicModifier(modifierKinds);
        case 'unused':
            return this.checkUnusedModifier(node);
        default:
            return true;
    }
  }

  private checkSimpleModifier(modifier: Modifier, modifierKinds: Set<number>): boolean {
    const modifierMap: { [key in Modifier]?: number } = {
        'abstract': ts.SyntaxKind.AbstractKeyword,
        'async': ts.SyntaxKind.AsyncKeyword,
        'exported': ts.SyntaxKind.ExportKeyword,
        'private': ts.SyntaxKind.PrivateKeyword,
        'protected': ts.SyntaxKind.ProtectedKeyword,
        'readonly': ts.SyntaxKind.ReadonlyKeyword,
        'static': ts.SyntaxKind.StaticKeyword
    };
    
    const syntaxKind = modifierMap[modifier];
    return syntaxKind ? modifierKinds.has(syntaxKind) : false;
  }

  private checkConstModifier(node: ts.Node): boolean {
    return ts.isVariableDeclaration(node) &&
        node.parent &&
        ts.isVariableDeclarationList(node.parent) &&
        !!(node.parent.flags & ts.NodeFlags.Const);
  }

  private checkGlobalModifier(node: ts.Node): boolean {
    return !node.parent || ts.isSourceFile(node.parent);
  }

  private checkPrivateIdentifier(node: ts.Node): boolean {
    return ts.isPrivateIdentifier(node);
  }

  private checkPublicModifier(modifierKinds: Set<number>): boolean {
    return modifierKinds.has(ts.SyntaxKind.PublicKeyword) ||
        !(modifierKinds.has(ts.SyntaxKind.PrivateKeyword) ||
          modifierKinds.has(ts.SyntaxKind.ProtectedKeyword));
  }

  private checkUnusedModifier(node: ts.Node): boolean {
    if (ts.isParameter(node) || ts.isVariableDeclaration(node)) {
        return !this.findNodeReferences(node, node.parent);
    }
    return true;
  }

  private checkTypes(node: ts.Node, option: Option): boolean {
    if (!option.types || option.types.length === 0) {
      return true;
    }

    const nodeType = this.getNodeType(node);
    if (!nodeType) return false;

    return option.types.some((typeOption) => {
      switch (typeOption) {
        case 'array':
          return this.isArrayType(node);
        case 'boolean':
          return this.isBooleanType(node);
        case 'function':
          return this.isFunctionType(node);
        case 'number':
          return this.isNumberType(node);
        case 'string':
          return this.isStringType(node);
        default:
          return false;
      }
    });
  }

  private findNodeReferences(node: ts.Node, scope: ts.Node): boolean {
    let found = false;
    
    function visit(n: ts.Node): void {
      if (found) {
        return;
      }
      
      if (ts.isIdentifier(n) && n !== node && n.text === (node as any).name?.text) {
        found = true;
        return;
      }
      
      ts.forEachChild(n, visit);
    }
    
    ts.forEachChild(scope, visit);
    return found;
  }

  private getNodeType(node: ts.Node): string | undefined {
    if (ts.isVariableDeclaration(node) && node.type) {
      return node.type.getText();
    }
    
    if (ts.isParameter(node) && node.type) {
      return node.type.getText(); 
    }
    
    if (ts.isPropertyDeclaration(node) && node.type) {
      return node.type.getText();
    }
    
    return undefined;
  }

  private isArrayType(node: ts.Node): boolean {
    const type = this.getNodeType(node);
    return type ? /Array<.*>|.*\[\]/.test(type) : false;
  }

  private isBooleanType(node: ts.Node): boolean {
    const type = this.getNodeType(node);
    return type === 'boolean';
  }

  private isFunctionType(node: ts.Node): boolean {
    if (ts.isFunctionDeclaration(node) || ts.isMethodDeclaration(node) || 
        ts.isArrowFunction(node) || ts.isFunctionExpression(node)) {
      return true;
    }
    const type = this.getNodeType(node);
    return type ? /\=\>/.test(type) || type.startsWith('Function') : false;
  }

  private isNumberType(node: ts.Node): boolean {
    const type = this.getNodeType(node);
    return type === 'number';
  }

  private isStringType(node: ts.Node): boolean {
    const type = this.getNodeType(node);
    return type === 'string';
  }

  private checkNaming(
    node: ts.Node,
    name: string,
    config: Option,
    kind: string
  ) {
    const identifier = name.split(':')[0].trim();
    name = identifier;
    const line = this.getNodePosition(node)[0];
    const character = node.getText();
    const violations: string[] = [];
    const isCopy = this.selectors.some(
      (item, index) => this.selectors.indexOf(item) !== index
    );

    // 如果节点已经被检查过，则直接返回
    if (this.isAlreadyChecked(character, line)) {
      return;
    }

    // 使用责任链模式执行检查
    this.runNamingChecks(
      node, name, config, kind, isCopy, character, line, violations
    );

    // 处理任何违规
    if (violations.length > 0) {
      this.handleViolations(node, violations);
    }
  }

  private runNamingChecks(node: ts.Node, name: string, config: Option, kind: string,
    isCopy: boolean, character: string, line: number, violations: string[]): void {
    const checks = [
      {
        condition: (): boolean | undefined => config.types && config.types.length > 0,
        check: (): boolean => this.checkTypesCondition(node, config)
      },
      {
        condition: (): boolean | undefined => config.modifiers && config.modifiers.length > 0,
        check: (): boolean => this.checkModifiersCondition(node, config)
      },
      {
        condition: (): boolean => !!config.custom, check: (): boolean => this.checkCustomRegex(name, config, kind, violations)
      },
      {
        condition: (): boolean => !!config.filter, check: (): boolean => this.checkFilter(name, config, kind, violations)
      },
      {
        condition: (): boolean => !!config.leadingUnderscore,
        check: (): boolean => this.checkLeadingUnderscore(name, config, kind, violations)
      },
      {
        condition: (): boolean => !!config.trailingUnderscore,
        check: (): boolean => this.checkTrailingUnderscore(name, config, kind, violations)
      },
      {
        condition: (): boolean => !!config.prefix?.length,
        check: (): boolean => this.checkPrefix(node, name, config, kind, isCopy, character, line, violations)
      },
      {
        condition: (): boolean => !!config.suffix?.length,
        check: (): boolean => this.checkSuffix(node, name, config, kind, isCopy, character, line, violations)
      },
      {
        condition: (): boolean => !!config.format?.length,
        check: (): boolean => {
          this.checkFormat(name, config, kind, isCopy, violations);
          return false;
        }
      }
    ];

    for (const { condition, check } of checks) {
      if (condition() && check()) {
        break;
      }
    }
  }

  private isAlreadyChecked(character: string, line: number): boolean {
    let isChecked = false;
    this.checkParamMap.forEach((value, key) => {
      if (key === character && value === line) {
        isChecked = true;
      }
    });
    return isChecked;
  }

  private checkTypesCondition(node: ts.Node, config: Option): boolean {
    return !this.checkTypes(node, config);
  }

  private checkModifiersCondition(node: ts.Node, config: Option): boolean {
    return !this.checkModifiers(node, config);
  }

  private checkCustomRegex(name: string, config: Option, kind: string, violations: string[]): boolean {
    try {
      if (!config.custom) {
        return false;
      }
      
      const customRegex = new RegExp(config.custom.regex);
      const matchesCustom = customRegex.test(name);
      
      if (config.custom.match) {
        if (!matchesCustom) {
          violations.push(
            `${kind} name '${name}' Must match custom pattern: ${config.custom.regex}`
          );
          return true;
        }
      } else {
        if (matchesCustom) {
          violations.push(
            `${kind} name '${name}' Must not match custom pattern: ${config.custom.regex}`
          );
          return true;
        }
      }
      return false;
    } catch (e) {
      violations.push(
        `${kind} name '${name}' Invalid custom regex pattern: ${config.custom?.regex || 'unknown'}`
      );
      return true;
    }
  }

  private checkFilter(name: string, config: Option, kind: string, violations: string[]): boolean {
    try {
      if (!config.filter) {
        return false;
      }
      
      let filterMatch = true;
      if (typeof config.filter === 'string') {
        const filterRegex = new RegExp(config.filter);
        filterMatch = filterRegex.test(name);
        return filterMatch;
      } else {
        let isNeedMatch = config.filter.match;
        const filterRegex = new RegExp(config.filter.regex);
        filterMatch = filterRegex.test(name);
        if (isNeedMatch) {
          return !filterMatch;
        } else {
          return filterMatch;
        }
      }
    } catch (e) {
      violations.push(`${kind} name '${name}' Invalid filter regex pattern`);
      return true;
    }
  }

  private checkLeadingUnderscore(name: string, config: Option, kind: string, violations: string[]): boolean {
    if (!config.leadingUnderscore) {
      return false;
    }
    const hasLeadingUnderscore = /^_/.test(name);
    const hasDoubleLeadingUnderscore = /^__/.test(name);
    switch (config.leadingUnderscore) {
      case 'forbid':
        if (hasLeadingUnderscore) {
          violations.push(
            `${kind} name '${name}' Leading underscore is forbidden`
          );
          return true;
        }
        break;
      case 'require':
        if (!hasLeadingUnderscore || hasDoubleLeadingUnderscore) {
          violations.push(
            `${kind} name '${name}' Single leading underscore is required`
          );
          return true;
        }
        break;
      case 'requireDouble':
        if (!hasDoubleLeadingUnderscore) {
          violations.push(
            `${kind} name '${name}' Double leading underscore is required`
          );
          return true;
        }
        break;
      case 'allowDouble':
        if (hasLeadingUnderscore && !hasDoubleLeadingUnderscore) {
          violations.push(
            `${kind} name '${name}' Only double leading underscore is allowed`
          );
          return true;
        }
        break;
    }
    
    return false;
  }

  private checkTrailingUnderscore(name: string, config: Option, kind: string, violations: string[]): boolean {
    if (!config.trailingUnderscore) { 
      return false; 
    }
    const hasTrailingUnderscore = /_$/.test(name);
    const hasDoubleTrailingUnderscore = /__$/.test(name);
    switch (config.trailingUnderscore) {
      case 'forbid':
        if (hasTrailingUnderscore) {
          violations.push(`${kind} name '${name}' Trailing underscore is forbidden`);
          return true;
        }
        break;
      case 'require':
        if (!hasTrailingUnderscore || hasDoubleTrailingUnderscore) {
          violations.push(`${kind} name '${name}' Single trailing underscore is required`);
          return true;
        }
        break;
      case 'requireDouble':
        if (!hasDoubleTrailingUnderscore) {
          violations.push(`${kind} name '${name}' Double trailing underscore is required`);
          return true;
        }
        break;
      case 'allowDouble':
        if (hasTrailingUnderscore && !hasDoubleTrailingUnderscore) {
          violations.push(`${kind} name '${name}' Only double trailing underscore is allowed`);
          return true;
        }
        break;
    }
    
    return false;
  }

  private checkPrefix(
    node: ts.Node, 
    name: string, 
    config: Option, 
    kind: string, 
    isCopy: boolean, 
    character: string, 
    line: number, 
    violations: string[]
  ): boolean {
    if (!config.prefix?.length) { 
      return false;
    }
    const prefixArr = config.prefix;
    let isChecked = this.isAlreadyChecked(character, line);
    if (isChecked) { 
      return true; 
    }
    if (!node.flags) {
       return false; 
    }
    const valueName = name;
    let hasPrefix: Boolean = false;
    if (isCopy) {
      this.ruleConfigs.prefixes.forEach((prefix) => {
        if (valueName.startsWith(prefix)) {
          hasPrefix = true;
        }
      });
      prefixArr.forEach((prefix) => {
        if (valueName.startsWith(prefix)) {
          hasPrefix = true;
        }
      });
    } else {
      prefixArr.forEach((prefix) => {
        if (valueName.startsWith(prefix)) {
          hasPrefix = true;
        }
      });
    }
    if (!hasPrefix) {
      violations.push(`${kind} name '${name}' '${config.prefix}' Must start with one of: ${prefixArr.join(', ')}`);
      this.checkParamMap.set(node.getText(), line);
      return true;
    }
    if (config.types && hasPrefix) {
      const [newLine, newCharacter] = this.getNodePosition(node);
      this.checkParamMap.set(node.getText(), newLine);
    }
    return false;
  }

  private checkSuffix(
    node: ts.Node, 
    name: string, 
    config: Option, 
    kind: string, 
    isCopy: boolean, 
    character: string, 
    line: number, 
    violations: string[]
  ): boolean {
    if (!config.suffix?.length) {
      return false; 
    }
    const suffixArr = config.suffix;
    let isChecked = this.isAlreadyChecked(character, line);
    if (isChecked) {
      return true; 
    }
    if (!node.flags) {
      return false;
    }
    const valueName = name;
    let hasSuffix: Boolean = false;

    if (isCopy) {
      this.ruleConfigs.suffixes.forEach((suffix) => {
        if (valueName.endsWith(suffix)) {
          hasSuffix = true;
        }
      });
      suffixArr.forEach((suffix) => {
        if (valueName.endsWith(suffix)) {
          hasSuffix = true;
        }
      });
    } else {
      suffixArr.forEach((suffix) => {
        if (valueName.endsWith(suffix)) {
          hasSuffix = true;
        }
      });
    }
    if (!hasSuffix) {
      violations.push(`${kind} name '${name}' Must end with one of: ${suffixArr.join(', ')}`);
      this.checkParamMap.set(node.getText(), line);
      return true;
    }
    if (config.types && hasSuffix) {
      const [newLine, newCharacter] = this.getNodePosition(node);
      this.checkParamMap.set(node.getText(), newLine);
    }
    return false;
  }

  private checkFormat(name: string, config: Option, kind: string, isCopy: boolean, violations: string[]): void {
    if (!config.format?.length) {
      return;
    }
    let nameWithoutAffixes = name.replace(/^_+|_+$/g, '');
    if (config.prefix && config.prefix.length > 0) {
      const prefixesArr = [...this.ruleConfigs.prefixes, ...config.prefix];
      for (const prefix of prefixesArr) {
        if (nameWithoutAffixes.startsWith(prefix)) {
          nameWithoutAffixes = nameWithoutAffixes.slice(prefix.length);
          break;
        }
      }
    }
    if (config.suffix && config.suffix.length > 0) {
      for (const suffix of this.ruleConfigs.suffixes) {
        if (nameWithoutAffixes.endsWith(suffix)) {
          nameWithoutAffixes = nameWithoutAffixes.slice(0, -suffix.length);
          break;
        }
      }
    }
    const formatRegex: { [key in Format]: RegExp } = {
      camelCase: /^[a-z][a-zA-Z0-9]*$/,
      strictCamelCase: /^[a-z](?:(?:[a-z0-9]*?[A-Z](?<![A-Z]))?[a-z0-9]*)*$/,
      PascalCase: /^[A-Z][a-zA-Z0-9]*$/,
      StrictPascalCase: /^[A-Z][a-z]+(?:[A-Z][a-z]+)*$/,
      snake_case: /^[a-z]+(_[a-z0-9]+)*$/,
      UPPER_CASE: /^[A-Z][A-Z0-9]*(_[A-Z0-9]+)*$/,
    };
    let formatMatches = false;
    if (isCopy) {
      const formats = [...config.format, ...this.formats];
      if (formats.length > this.formats.length) {
        formatMatches = formats.some((format) =>
          formatRegex[format].test(nameWithoutAffixes)
        );
      }
    } else {
      formatMatches = config.format.some((format) =>
        formatRegex[format].test(nameWithoutAffixes)
      );
    }
    if (!formatMatches) {
      violations.push(`${kind} name '${name}' must match one of the following formats: ${config.format.join(', ')}`);
    }
  }

  private handleViolations(node: ts.Node, violations: string[]): void {
    const position = this.getNodePosition(node);
    const line = position[0];
    const column = position[1];
    const endColumn = node.getEnd();

    violations.forEach((violation) => {
      const description = `${violation}`;
      const severity = this.rule.alert ?? this.metaData.severity;
      const ruleName = '';
      const fileName = this.currentArkFile?.getFilePath() ?? '';

      const defect = new Defects(
        line,
        column,
        endColumn,
        `${description} ${ruleName}`,
        severity,
        this.rule.ruleId,
        fileName,
        this.metaData.ruleDocPath,
        true,
        false,
        false
      );

      this.issues.push(new IssueReport(defect, undefined));
      RuleListUtil.push(defect);
    });
  }

  private getNodePosition(node: ts.Node): [number, number] {
    if (!this.currentArkFile) {
        return [0, 0];
    }
    const sourceFile = node.getSourceFile();
    if (!sourceFile) {
        return [0, 0];
    }

    let pos: number;
    
    if (ts.isClassDeclaration(node) || ts.isInterfaceDeclaration(node) || 
        ts.isEnumDeclaration(node) || ts.isTypeAliasDeclaration(node)) {
        pos = node.name?.getStart() ?? node.getStart();
    } else if (ts.isMethodDeclaration(node) || ts.isFunctionDeclaration(node) ||
               ts.isPropertyDeclaration(node) || ts.isGetAccessor(node) || 
               ts.isSetAccessor(node)) {
        pos = node.name?.getStart() ?? node.getStart();
    } else if (ts.isVariableDeclaration(node)) {
        pos = node.name.getStart();
    } else if (ts.isParameter(node)) {
        pos = ts.isIdentifier(node.name) ? node.name.getStart() : node.getStart();
    } else if (ts.isIdentifier(node)) {
        pos = node.getStart();
    } else {
        pos = node.getStart();
    }

    const { line, character } = sourceFile.getLineAndCharacterOfPosition(pos);
    return [line + 1, character + 1];
  }
}